"""
认证模块 - 声明式权限校验
"""
from typing import Callable

from fastapi import Depends, Request

from axiom_boot.auth.models import Principal
from axiom_boot.core.exceptions import PermissionDenied, Unauthorized
from axiom_boot.auth.security import get_current_principal, get_token_payload
from axiom_boot.logging.setup import get_logger

logger = get_logger(__name__)


class PermissionChecker:
    """
    一个可依赖注入的权限检查器类。
    它会验证当前认证主体的令牌是否被授予了访问端点所需的权限。
    """
    def __init__(self, permission: str):
        if not permission:
            raise ValueError("权限代码不能为空")
        self.permission = permission
        logger.debug(f"创建权限检查器，需要权限: '{self.permission}'")

    async def __call__(
        self,
        request: Request,
        principal: Principal = Depends(get_current_principal),
        token_payload: dict = Depends(get_token_payload)
    ):
        """
        执行权限检查的核心逻辑。
        检查顺序:
        1. 如果用户角色为 'ADMIN'，则自动通过。
        2. 检查 JWT 令牌的 'scope' 声明是否包含所需权限。
        3. 如果 'scope' 不存在或不包含，则回退到检查用户的完整 'permissions' 列表。
        """
        settings = request.app.context._settings
        if not settings.oauth_enabled:
            logger.debug("OAuth 已禁用，跳过权限检查。")
            return

        # principal 和 token_payload 由 Depends 保证存在，否则会先抛出 Unauthorized
        
        logger.debug(f"执行权限检查，用户: '{principal.username}', 需要权限: '{self.permission}'")

        if "ADMIN" in principal.roles:
            logger.debug(f"用户 '{principal.username}' 是 ADMIN，自动授予权限。")
            return

        # 优先检查令牌的 scope，这是 OAuth 2.0 的标准实践
        token_scopes = token_payload.get("scope", [])
        if self.permission in token_scopes:
            logger.debug(f"权限校验通过 (来自令牌 scope)。用户: '{principal.username}', 权限: '{self.permission}'")
            return

        # 如果 scope 中没有，作为备用方案，检查用户的完整权限列表
        # 这对于非OAuth流程或需要绕过scope限制的内部调用可能有用
        if self.permission in principal.permissions:
            logger.debug(f"权限校验通过 (来自用户完整权限列表)。用户: '{principal.username}', 权限: '{self.permission}'")
            return

        logger.warning(
            f"用户 '{principal.username}' 权限校验失败。需要权限: '{self.permission}'. "
            f"令牌 Scopes: {token_scopes}, 用户总权限: {principal.permissions}"
        )
        raise PermissionDenied(f"缺少所需权限: {self.permission}")


def pre_authorize(permission: str) -> Callable:
    """
    一个依赖项工厂函数，用于创建基于特定权限的依赖项。
    这是 FastAPI 中实现声明式权限校验的推荐方式。

    用法:
        from fastapi import Depends

        @router.get(
            "/some-resource",
            dependencies=[Depends(pre_authorize("sys:user:list"))]
        )
        async def read_resource(...):
            ...
    
    Args:
        permission: 所需的权限代码字符串 (e.g., "sys:user:create")。

    Returns:
        一个 FastAPI 可识别的依赖项，它会执行权限检查。
    """
    return PermissionChecker(permission)
